package midend.IR_Optimizer;

import com.sun.jdi.Value;
import config.Config;
import midend.CFG_Builder;
import midend.IR_Builder;
import midend.IR_Type.*;
import midend.IR_Module;
import midend.IR_Instruction.*;
import midend.IR_Value.IR_BaseBlock;
import midend.IR_Value.IR_FuncBlock;
import midend.IR_Value.IR_Value;
import midend.IR_Value.IR_VirtualReg_Value;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

import static midend.IR_Builder.IR_Atendwithprenext;
import static midend.IR_Builder.IR_deletewithprenext;

public class Func_Inline {
    private IR_Module module;
    private boolean changed = true;
    private IR_Builder builder = new IR_Builder();

    public Func_Inline(IR_Module module) {
        this.module = module;
    }

    public void Optimize(){
        ArrayList<IR_FuncBlock> inLine_Funcs = new ArrayList<>();
        //找到能内联的函数
        while(changed) {
            module.buildCallRelation();
            changed = false;
            for (IR_FuncBlock function : module.getFuncBlocks()) {
                if (function.isInLineable()) {
                    inLine_Funcs.add(function);
                }
            }
            for (IR_FuncBlock function : inLine_Funcs) {
                inLineFunc(function);
            }
            inLine_Funcs.clear();
            //module.outputLLVM(Config.llvmoptFile);
            //new CFG_Builder(module).run();
        }
        module.buildCallRelation();
        module.removeNoCallFunc();
    }

    public void inLineFunc(IR_FuncBlock function) {
        if(function.getBeCalledList().isEmpty()) {
            return;
        }
        changed = true;
        //找到对应的callInstr
        ArrayList<Instruction> callInstr = new ArrayList<>();
        for (IR_FuncBlock function1 : function.getBeCalledList()) {
            if (function1.equals(function)) {
                continue;
            }
            for (IR_BaseBlock block : function1.get_bb_List()) {
                for (Instruction instruction : block.get_Instruction_List()) {
                    if (instruction instanceof Call_Instruction) {
                        if (((Call_Instruction) instruction).get_funcBlock().get_FuncName().equals(function.get_FuncName())) {
                            callInstr.add(instruction);
                        }
                    }
                }
            }
        }

        //处理callInstr进行替换
        for (Instruction instruction : callInstr) {
            dealCall((Call_Instruction) instruction);
        }

        //删除该函数调用表
        function.getBeCalledList().clear();
    }

    //假设函数中块关系为preBlock-InlineBlock-nextBlock
    //InlineBlock分为beforeFuncBlock（InlineBlock）-funcBlock-afterBlock
    //1.构建afterBlock
    //2.删除callInstr及后继指令
    //3.修正基本块的前驱后继关系（pre,nextb）
    //4.替换形参为实参
    //5.处理ret\call以及储存内联函数的块
    //6.ret只有一个可以直接将call替换为相应的Value跳转到nextBlock
    //7.多个ret可使用phi指令
    //8.加入内联函数块（即funcBlock）
    //9.函数中的call构建新的函数关系
    public void dealCall(Call_Instruction callInstr) {
        IR_BaseBlock InlineBlock = callInstr.get_bb();
        IR_FuncBlock InlineFunc = callInstr.get_funcBlock();
        IR_FuncBlock nowFunc = InlineBlock.get_FuncBlock();

        //1.构建afterBlock inlineblock-funcblock-afterblock
        IR_BaseBlock afterBlock = new IR_BaseBlock("afterblock", nowFunc);
        LinkedList<IR_BaseBlock> basicBlocks = nowFunc.get_bb_List();
        int index = basicBlocks.indexOf(InlineBlock);
        basicBlocks.add(index + 1, afterBlock);

        //2.在afterBlock中加入指令
        int instrIndex = InlineBlock.get_Instruction_List().indexOf(callInstr);
        LinkedList<Instruction> instructions = new LinkedList<>(InlineBlock.get_Instruction_List());
        for (int count = instrIndex + 1; count < instructions.size(); count++) {
            Instruction instruction = instructions.get(count);
            afterBlock.add_InstructionList(instruction);
            instruction.set_bb(afterBlock);
            InlineBlock.get_Instruction_List().remove(instruction);
        }
        //InlineBlock.getInstrList().remove(callInstr);
        callInstr.remove();
        nowFunc.getCalledList().remove(InlineFunc);
        InlineFunc.getBeCalledList().remove(nowFunc);

        //3.维持前驱后续
//        for (IR_BaseBlock block : InlineBlock.getNextBlock()) {
//            afterBlock.getNextBlock().add(block);
//            for (int count = 0; count < block.getPreBlock().size(); count++) {
//                IR_BaseBlock basicBlock = block.getPreBlock().get(count);
//                if (basicBlock == InlineBlock) {
//                    block.getPreBlock().set(count, afterBlock);
//                }
//            }
//        }
        List<IR_BaseBlock> ll = new ArrayList<>(InlineBlock.getNextBlock());
        for (IR_BaseBlock block : ll) {
            IR_deletewithprenext(InlineBlock,block);
            IR_Atendwithprenext(afterBlock,block);
            dealwithPhi(block,InlineBlock,afterBlock);
        }
        //IR_Atendwithprenext(InlineBlock,afterBlock);


        //4.在InlineBlock和afterBlock之间构造FuncBlock
        IR_FuncBlock copyFunc = InlineFunc.clone();
        IR_BaseBlock FuncBlock = copyFunc.get_bb_List().getFirst();
        IR_Builder.setbuilderbb(builder,InlineBlock);
        IR_Builder.Br(builder, FuncBlock);
        InlineBlock.add_NextBlock(FuncBlock);
        FuncBlock.add_PreBlock(InlineBlock);

        IR_Atendwithprenext(InlineBlock,FuncBlock);
        IR_Atendwithprenext(FuncBlock,afterBlock);

        //5.将形参替换为实参
        ArrayList<IR_Value> formalParams = (ArrayList<IR_Value>) copyFunc.get_params();
        ArrayList<IR_Value> actualParams = (ArrayList<IR_Value>) callInstr.get_params();
        for (int count = 0; count < formalParams.size(); count++) {
            IR_Value formalParam = formalParams.get(count);
            IR_Value actualParam = actualParams.get(count);
            if (actualParam.get_Type() instanceof IR_Float || actualParam.get_Type() instanceof IR_Int32) {
                copyFunc.replaceUseInFunc(formalParam, actualParam);
            } else {
                ArrayList<Instruction> delete = new ArrayList<>();
                for (Instruction user : copyFunc.get_UserList_InFunc(formalParam)) {
                    if (user instanceof Store_Instruction) {
                        IR_Value pointer = user.get_op().get(2);
                        delete.add(copyFunc.find_Alloc_Instruciton(pointer));
                        delete.add(user);
                        for (Instruction user1 : copyFunc.get_UserList_InFunc(pointer)) {
                            if (user1 instanceof Load_Instruction) {
                                copyFunc.replaceUseInFunc(user1.get_op().get(0), actualParam);
                                delete.add((Load_Instruction) user1);
                            }
                        }
                    }
                    else if(user instanceof Phi_Instruction) {
                        for (IR_BaseBlock key : ((Phi_Instruction)user).get_ComingValues().keySet()) {
//                            //自己里面的变化不进行修改
//                            if(value.equals(phiInstruction.get_op_index(0))){
//                                continue;
//                            }
                            if (((Phi_Instruction)user).get_ComingValues().get(key).equals(formalParam)) {
                                ((Phi_Instruction)user).set_ComingValue(actualParam, key);
                            }
                        }
                    }
                }
                for (Instruction instruction1 : delete) {
                    instruction1.remove();
                }
            }
        }
        //6.统计ret和call指令
        // 记录copyFunc中的所有基本块
        ArrayList<IR_BaseBlock> copy_FuncBlock = new ArrayList<>();
        ArrayList<Instruction> ret = new ArrayList<>();
        ArrayList<Instruction> call = new ArrayList<>();
        for (IR_BaseBlock block : copyFunc.get_bb_List()) {
            copy_FuncBlock.add(block);
            for (Instruction instruction : block.get_Instruction_List()) {
                if (instruction instanceof Ret_Instruction) {
                    ret.add(instruction);
                    continue;
                }
                if (instruction instanceof Call_Instruction) {
                    call.add(instruction);
                    continue;
                }
            }
        }

        //7.处理ret指令（只有一个则直接跳到afterBlock）,多个需要利用phi指令
        if (copyFunc.get_RetType() instanceof IR_Int32 || copyFunc.get_RetType() instanceof IR_Float) {
            if (ret.size() == 1) {
                Ret_Instruction retInstr = (Ret_Instruction) ret.get(0);
                IR_BaseBlock block = retInstr.get_bb();
                nowFunc.replaceUseInFunc(callInstr.get_op().get(0), retInstr.get_op().get(0));
                retInstr.remove();
                // 让copyrFunc中的ret指令所在的块，跳转到afterBlock
                IR_Builder.setbuilderbb(builder, block);
                IR_Builder.Br(builder, afterBlock);
                block.add_NextBlock(afterBlock);
                afterBlock.add_PreBlock(block);
            } else {
                //TODO:多个ret,使用phi指令
                IR_Value res = new IR_VirtualReg_Value("phi", InlineFunc.get_RetType());
                Phi_Instruction phiInstr = new Phi_Instruction(Collections.singletonList(res), afterBlock);
                LinkedList<Instruction> instructions1 = afterBlock.get_Instruction_List();
                for (Instruction instruction : ret) {
                    IR_BaseBlock nowBlock = instruction.get_bb();
                    IR_Value addValue = instruction.get_op().get(0);
                    if (((Ret_Instruction) instruction).get_op().get(0).get_Type() instanceof IR_Void) {
                        continue;
                    }
                    if (IR_FuncBlock.cloneMap_Value.containsKey(addValue)) {
                        addValue = IR_FuncBlock.cloneMap_Value.get(addValue);
                    }
                    phiInstr.set_ComingValue(addValue, instruction.get_bb());
                    instruction.remove();
                    //
                    IR_Builder.setbuilderbb(builder,nowBlock);
                    IR_Builder.Br(builder, afterBlock);
                    nowBlock.add_NextBlock(afterBlock);
                    afterBlock.add_PreBlock(nowBlock);
                }
                nowFunc.replaceUseInFunc(callInstr.get_op().get(0), res);
                instructions1.addFirst(phiInstr);
            }
        } else if (copyFunc.get_RetType()instanceof IR_Void) {
            for (Instruction instruction : ret) {
                IR_BaseBlock block = instruction.get_bb();
                instruction.remove();
                // 有问题
                IR_Builder.setbuilderbb(builder,block);
                IR_Builder.Br(builder, afterBlock);
                block.add_NextBlock(afterBlock);
                afterBlock.add_PreBlock(block);
            }
        }

        //8.将copy_FuncBlock加入到调用者中
        for (IR_BaseBlock block : copy_FuncBlock) {
            block.set_FuncBlock(nowFunc);
            int index0 = basicBlocks.indexOf(afterBlock);
            basicBlocks.add(index0, block);
        }

        //9.将copy_Func中所有的调用者设置为nowFunc
        for (Instruction instruction : call) {
            Call_Instruction callInstr1 = ((Call_Instruction) instruction);
            IR_FuncBlock callFunc = callInstr1.get_funcBlock();
            if (callFunc.isLib()) {
                continue;
            }
            callFunc.getBeCalledList().add(nowFunc);
            nowFunc.getCalledList().add(callFunc);
        }

        module.getFuncBlocks().remove(copyFunc);
    }

    private void dealwithPhi(IR_BaseBlock block,IR_BaseBlock oldpre,IR_BaseBlock newpre){
        for(Instruction instruction : block.get_Instruction_List()){
            if(instruction instanceof Phi_Instruction phiInstruction){
                if(phiInstruction.get_ComingValues().containsKey(oldpre)){
                    IR_Value value = phiInstruction.get_ComingValues().get(oldpre);
                    phiInstruction.remove_ComingValues(oldpre);
                    phiInstruction.set_ComingValue(value, newpre);
                }
            }
            else{
                break;
            }
        }
    }

}
